﻿//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description: Validation Rule to validate multiple cookie name-value pairs are 
//              present as part of the response or request 
//
//------------------------------------------------------------------------------
using System;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting;

namespace WebAndLoadTestPluginsAndRules
{
    [DisplayName("Multiple Cookies")]
    [Description("Validates multiple cookie name-value pairs are present as part of the response or request")]
    public class ValidateMultipleCookies : ValidationRule
    {
        // The Cookies
        private string cookies;

        [DisplayName("Cookies")]
        [Description("The cookies expected in the web response or request. \nThe format is cookie-name1=cookie-value1;cookie-name2=cookie-value2;cookie-name3=cookie-value3\nPlease ensure that there's no empty leading or trailing spaces for each cookie name-value pair")]
        public string RequiredCookies
        {
            get { return cookies; }
            set { cookies = value; }
        }

        // The delimiter
        private char[] delimiter = {';', ','};

        // Cookie is part of Request or Response
        private bool responseValue = true;

        [DisplayName("Cookie is part of Response")]
        [Description("Whether the cookie is part of the Request or Response")]
        [DefaultValue(true)]
        public bool Response
        {
            get { return responseValue; }
            set { responseValue = value; }
        }

        //  Should the Case be ignored while comparing the cookie value
        private bool ignoreCaseValue = false;

        [DisplayName("Ignore Case")]
        [Description("Whether case should be ignored or not while comparing the cookie value.")]
        [DefaultValue(false)]
        public bool IgnoreCase
        {
            get { return ignoreCaseValue; }
            set { ignoreCaseValue = value; }
        }
    
        // Validate is called with the test case Context and the request context.
        // These allow the rule to examine both the request and the response.
        //---------------------------------------------------------------------
        public override void Validate(object sender, ValidationEventArgs e)
        {
            bool validated = false, 
                 overallValidation = true,
                 cookieFound = false, 
                 cookieValueFound = false;

            System.Net.Cookie myCookie;

            string cookieName = String.Empty,
                   cookieValue = String.Empty,
                   errorString = String.Empty;
                   
            string[] tokens, 
                     cookiePair;
            
            try
            {

                if (cookies.Length != 0)
                {
                    //  It's doesn't matter if it's an XML or HTML response
                    if ((Response == true && e.Response.Cookies.Count > 0) ||
                        (Response == false && e.Request.Cookies.Count > 0))
                    {

                        tokens = cookies.Split(delimiter);

                        if (tokens == null)
                        {
                            throw new NullReferenceException("The tokens obtained using the specified delimiter were null");
                        }

                        for (int i = 0; i < tokens.Length; i++)
                        {
                            validated = false;
                            cookieFound = false;
                            cookieValueFound = false;

                            cookiePair = tokens[i].Split('=');

                            if (cookiePair == null)
                            {
                                throw new NullReferenceException("The cookie pair obtained using the delimiter '=' was null");
                            }

                            if (cookiePair.Length < 2 || cookiePair.Length > 2)
                            {
                                cookieName = String.Empty;
                                cookieValue = String.Empty;
                            }
                            else
                            {
                                cookieFound = true;
                                cookieName = cookiePair[0];
                                cookieValue = cookiePair[1];
                            }

                            if (cookieName != null && (myCookie = GetCookie(e, cookieName)) != null)
                            {
                                if (myCookie.Value.Equals(cookieValue, (IgnoreCase == true) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))
                                {
                                    cookieValueFound = true;
                                    validated = true;
                                }
                                else
                                {
                                    cookieValueFound = false;
                                }
                            }

                            // If the validation fails, set the error text that the user sees
                            if (!validated)
                            {
                                overallValidation = validated;

                                if (cookieFound == false)
                                {
                                    if (cookiePair.Length < 2)
                                    {
                                        errorString = String.Concat(errorString, String.Format("\r\nThe = sign is not present for the cookie '{0}'", tokens[i]));
                                    }
                                    else if (cookiePair.Length > 2)
                                    {
                                        errorString = String.Concat(errorString, String.Format("\r\nThe = is present more than once for the cookie '{0}'", tokens[i]));
                                    }
                                }
                                else if (cookieValueFound == false)
                                {
                                    errorString = String.Concat(errorString, String.Format("\r\nCould not find the expected cookie value '{0}' for the cookie name '{1}'", cookieValue, cookieName));
                                }
                            }
                        }

                        e.IsValid = overallValidation;
                        e.Message = String.Copy(errorString);

                    }
                    else
                    {
                        e.IsValid = false;
                        e.Message = String.Format("There are no cookies in this web {0}", (Response == true)? "response" : "request");
                    }
                }
                else
                {
                    e.IsValid = false;
                    e.Message = String.Format("Cookies cannot be empty");
                }
            }
            catch (Exception ex)
            {
                e.IsValid = validated;
                e.Message = String.Format("An exception occurred. Cookie string: '{0}' and delimiter: '{1}'. Stack Dump: {2}", cookies, delimiter, ex);
                
            }            
        }

        private System.Net.Cookie GetCookie(ValidationEventArgs E, string Name)
        {
            if (Response == false) 
            {
                return E.Request.Cookies[Name];
            }
            
            return E.Response.Cookies[Name];
        }
        
    }
}